package org.opendatakit.sensors.wifi;

import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Set;

import org.opendatakit.sensors.wifi.MulticastService.LocalBinder;

import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.IBinder;
import android.util.Log;
import java.io.*;
import java.net.*;
import java.util.*;   
import org.apache.http.conn.util.InetAddressUtils;

public class WifiAdapter extends Activity{
	
    private static final String TAG = "WifiAdapter";

	private static WifiAdapter adapter = null;
	private static Context context;
	public static final int  port = 50000;
	MulticastService mService;
    boolean mBound = false;
    private static String mac;
	private HashMap<String,WifiDevice> deviceMap;
    
	public static final String ACTION_DISCOVERY_FINISHED = "Wifi.Adapter.action.discovery.finished"; 
	public static WifiAdapter getDefaultAdapter(Context context){
		
		//TODO : Add functionality to return adapter only if Wifi functionality present in phone
		if(adapter==null){
			adapter = new WifiAdapter(context);
			android.net.wifi.WifiManager wifiMan = (android.net.wifi.WifiManager) context.getSystemService(
			        Context.WIFI_SERVICE);
			WifiInfo wifiInf = wifiMan.getConnectionInfo();
		    mac = wifiInf.getMacAddress();
			return adapter;
		}
		else{
			return adapter;
		}
	}
	
	//Discovery Message
	public static String getPacketData(){
		String dataPacket = "{"+
	             "\"exec\": {"+
	             "\"sdr\": \""+mac+"\","+
	             "\"rcv\": \"ff:ff:ff:ff:ff:ff\","+
	            "\"time\": \""+System.currentTimeMillis()+"\","+
	            "\"port\": \""+ port +"\","+
	            "\"ip\": \""+ getIPAddress(true) +"\","+
	            "\"op\": 0,"+
	            "\"ack\": true,"+
	            "\"argv\": ["+
	                "    {\"type\": 1, \"val\": 5}"+
	                "]"+
	        "}"+
	        "}";
		return dataPacket;
	}
	
	private WifiAdapter(Context ctx){
		deviceMap= new HashMap<String,WifiDevice>();
		context = ctx;
	}

	public void put(String id,WifiDevice w){
		deviceMap.put(id, w);
	}
	
	public Set<WifiDevice> getBondedDevices() {

		Set<WifiDevice> wifiDeviceList = new LinkedHashSet<WifiDevice>();
		
		if(mBound){
			wifiDeviceList = mService.getDeviceList();
		}
		
		return wifiDeviceList;
	}

	
	public boolean isDiscovering() {
		// TODO Auto-generated method stub
		return false;
	}

	public void cancelDiscovery() {
		
		if (mBound) {
            unbindService(mConnection);
            mBound = false;
        }
		
	}
	
	
	
	ServiceConnection mConnection = new ServiceConnection() {

        @Override
        public void onServiceConnected(ComponentName className, IBinder service) {
            // We've bound to LocalService, cast the IBinder and get LocalService instance
            Log.d("exception aa rahi hai","hello1");

            LocalBinder binder = (LocalBinder) service;
            Log.d("exception aa rahi hai","hello2");
            mService = binder.getService();
            Log.d("exception aa rahi hai","hello3");
            mBound = true;
            Log.d("exception aa rahi hai","hello"+ mService.getPackageName());
        }

        @Override
        public void onServiceDisconnected(ComponentName arg0) {
            mBound = false;
        }
    };

	public void startDiscovery() {
		
	}
	
	public WifiDevice getRemoteDevice(String id) {
		return deviceMap.get(id);
	}
   /**
	     * Convert byte array to hex string
	     * @param bytes
	     * @return
	     */
	    public static String bytesToHex(byte[] bytes) {
	        StringBuilder sbuf = new StringBuilder();
	        for(int idx=0; idx < bytes.length; idx++) {
	            int intVal = bytes[idx] & 0xff;
	            if (intVal < 0x10) sbuf.append("0");
	            sbuf.append(Integer.toHexString(intVal).toUpperCase());
	        }
	        return sbuf.toString();
	    }

	    /**
	     * Get utf8 byte array.
	     * @param str
	     * @return  array of NULL if error was found
	     */
	    public static byte[] getUTF8Bytes(String str) {
	        try { return str.getBytes("UTF-8"); } catch (Exception ex) { return null; }
	    }

	    /**
	     * Load UTF8withBOM or any ansi text file.
	     * @param filename
	     * @return  
	     * @throws java.io.IOException
	     */
	    public static String loadFileAsString(String filename) throws java.io.IOException {
	        final int BUFLEN=1024;
	        BufferedInputStream is = new BufferedInputStream(new FileInputStream(filename), BUFLEN);
	        try {
	            ByteArrayOutputStream baos = new ByteArrayOutputStream(BUFLEN);
	            byte[] bytes = new byte[BUFLEN];
	            boolean isUTF8=false;
	            int read,count=0;           
	            while((read=is.read(bytes)) != -1) {
	                if (count==0 && bytes[0]==(byte)0xEF && bytes[1]==(byte)0xBB && bytes[2]==(byte)0xBF ) {
	                    isUTF8=true;
	                    baos.write(bytes, 3, read-3); // drop UTF8 bom marker
	                } else {
	                    baos.write(bytes, 0, read);
	                }
	                count+=read;
	            }
	            return isUTF8 ? new String(baos.toByteArray(), "UTF-8") : new String(baos.toByteArray());
	        } finally {
	            try{ is.close(); } catch(Exception ex){} 
	        }
	    }

	    /**
	     * Returns MAC address of the given interface name.
	     * @param interfaceName eth0, wlan0 or NULL=use first interface 
	     * @return  mac address or empty string
	     */
	    public static String getMACAddress(String interfaceName) {
	        try {
	            List<NetworkInterface> interfaces = Collections.list(NetworkInterface.getNetworkInterfaces());
	            for (NetworkInterface intf : interfaces) {
	                if (interfaceName != null) {
	                    if (!intf.getName().equalsIgnoreCase(interfaceName)) continue;
	                }
	                byte[] mac = intf.getHardwareAddress();
	                if (mac==null) return "";
	                StringBuilder buf = new StringBuilder();
	                for (int idx=0; idx<mac.length; idx++)
	                    buf.append(String.format("%02X:", mac[idx]));       
	                if (buf.length()>0) buf.deleteCharAt(buf.length()-1);
	                return buf.toString();
	            }
	        } catch (Exception ex) { } // for now eat exceptions
	        return "";
	        /*try {
	            // this is so Linux hack
	            return loadFileAsString("/sys/class/net/" +interfaceName + "/address").toUpperCase().trim();
	        } catch (IOException ex) {
	            return null;
	        }*/
	    }

	    /**
	     * Get IP address from first non-localhost interface
	     * @param ipv4  true=return ipv4, false=return ipv6
	     * @return  address or empty string
	     */
	    public static String getIPAddress(boolean useIPv4) {
	        try {
	            List<NetworkInterface> interfaces = Collections.list(NetworkInterface.getNetworkInterfaces());
	            for (NetworkInterface intf : interfaces) {
	                List<InetAddress> addrs = Collections.list(intf.getInetAddresses());
	                for (InetAddress addr : addrs) {
	                    if (!addr.isLoopbackAddress()) {
	                        String sAddr = addr.getHostAddress().toUpperCase();
	                        boolean isIPv4 = InetAddressUtils.isIPv4Address(sAddr); 
	                        if (useIPv4) {
	                            if (isIPv4) 
	                                return sAddr;
	                        } else {
	                            if (!isIPv4) {
	                                int delim = sAddr.indexOf('%'); // drop ip6 port suffix
	                                return delim<0 ? sAddr : sAddr.substring(0, delim);
	                            }
	                        }
	                    }
	                }
	            }
	        } catch (Exception ex) { } // for now eat exceptions
	        return "";
	    }

	}
